package com.example.invoke.xsd.parser;

import com.example.invoke.xsd.ConfigSpringImpl;
import com.example.invoke.xsd.xml.ApplicationConfig;
import com.example.invoke.xsd.xml.ListenerConfig;
import com.example.invoke.xsd.xml.ServiceConfig;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

import java.util.ArrayList;
import java.util.List;

/**
 * bean定义信息的分析
 * ConfigSpringImpl implements InitializingBean可以把xsd中解析到的beanDefinition再次处理，形成扩展点
 */
public class UserDefinitionParser extends AbstractBeanDefinitionParser {

    @Override
    protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        BeanDefinitionBuilder parent = BeanDefinitionBuilder
                .rootBeanDefinition(ConfigSpringImpl.class)
                .setScope(BeanDefinition.SCOPE_SINGLETON)
                .setLazyInit(false);

        parseApplication(element, parserContext, parent);
        parseService(element, parserContext, parent);
        parseListener(element, parserContext, parent);

        return parent.getBeanDefinition();
    }

    private void parseListener(Element element, ParserContext parserContext, BeanDefinitionBuilder parent) {
        List<Element> listenerElementList = DomUtils.getChildElementsByTagName(element, "listener");
        if (listenerElementList == null || listenerElementList.size() < 1) {
            return;
        }
        List<BeanDefinition> beanDefinitionList = new ArrayList<>(listenerElementList.size());
        for (Element listenerElement: listenerElementList) {
            beanDefinitionList.add(doParseListener(listenerElement, parserContext));
        }
    }

    private BeanDefinition doParseListener(Element listenerElement, ParserContext parserContext) {

        BeanDefinitionBuilder listener = BeanDefinitionBuilder
                .rootBeanDefinition(ListenerConfig.class)
                .setScope(BeanDefinition.SCOPE_SINGLETON)
                .setLazyInit(false);

        String id = listenerElement.getAttribute("id");
        String version = listenerElement.getAttribute("version");
        String ip = listenerElement.getAttribute("ip");
        String port = listenerElement.getAttribute("port");
        String listenerName = listenerElement.getAttribute("listener");

        listener.addPropertyValue("id", id);
        listener.addPropertyValue("version", version);
        listener.addPropertyValue("ip", ip);
        listener.addPropertyValue("port", port);
        listener.addPropertyValue("listener", listenerName);

        String name = ListenerConfig.class.getName() + "#" + id + "#" + version;
        if (parserContext.getRegistry().containsBeanDefinition(name)) {
            throw new RuntimeException("duplicate listener load: " + name);
        }
        parserContext.getRegistry().registerBeanDefinition(name, listener.getBeanDefinition());
        return listener.getBeanDefinition();
    }

    /**
     * 解析application标签
     * @param element
     * @param parserContext
     * @param parent
     */
    private void parseApplication(Element element, ParserContext parserContext, BeanDefinitionBuilder parent) {
        Element applicationElement = DomUtils.getChildElementByTagName(element, "application");
        if (applicationElement == null) {
            return;
        }

        BeanDefinitionBuilder application = BeanDefinitionBuilder
                .rootBeanDefinition(ApplicationConfig.class)
                .setScope(BeanDefinition.SCOPE_SINGLETON)
                .setLazyInit(false);


        application.addPropertyValue("name", applicationElement.getAttribute("name"));
        application.addPropertyValue("description", applicationElement.getAttribute("description"));

        String name = ApplicationConfig.class.getName();
        if (parserContext.getRegistry().containsBeanDefinition(name)) {
            throw new RuntimeException("more than one application load");
        }
        AbstractBeanDefinition definition = application.getBeanDefinition();
        parserContext.getRegistry().registerBeanDefinition(name, definition);
        parent.addPropertyValue("applicationConfig", definition);
    }

    /**
     * 解析service标签，可以为list
     * @param element
     * @param parserContext
     * @param parent
     */
    private void parseService(Element element, ParserContext parserContext, BeanDefinitionBuilder parent) {

        List<Element> serviceElementList = DomUtils.getChildElementsByTagName(element, "service");
        if (serviceElementList == null || serviceElementList.size() < 1) {
            return;
        }
        List<BeanDefinition> beanDefinitionList = new ArrayList<>(serviceElementList.size());
        for (Element serviceElement: serviceElementList) {
            beanDefinitionList.add(doParseService(serviceElement, parserContext));
        }

    }

    private BeanDefinition doParseService(Element serviceElement, ParserContext parserContext) {
        BeanDefinitionBuilder service = BeanDefinitionBuilder
                .rootBeanDefinition(ServiceConfig.class)
                .setScope(BeanDefinition.SCOPE_SINGLETON)
                .setLazyInit(false);

        String serviceId = serviceElement.getAttribute("serviceId");
        String description = serviceElement.getAttribute("description");
        String version = serviceElement.getAttribute("version");

        service.addPropertyValue("serviceId", serviceId);
        service.addPropertyValue("description", description);
        service.addPropertyValue("version", version);

        String name = ServiceConfig.class.getName() + "#" + serviceId + "#" + version;
        if (parserContext.getRegistry().containsBeanDefinition(name)) {
            throw new RuntimeException("duplicate serviceId load: " + name);
        }
        parserContext.getRegistry().registerBeanDefinition(name, service.getBeanDefinition());
        return service.getBeanDefinition();
    }

    /**
     * 节点没有id也可正常读取
     * 默认必须添加id
     * @return
     */
    @Override
    protected boolean shouldGenerateId() {
        return true;
    }
}
